package com.waimai.app.qa.webdriver;

import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.Point;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.RemoteWebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import com.google.common.base.Function;
import com.waimai.app.qa.data.Item;
import com.waimai.app.qa.listener.ListenerAdapter;

import io.appium.java_client.AppiumDriver;
import io.appium.java_client.TouchAction;

public class AppElement {
	public Item item = null;
	private AppiumDriver driver = AppDriver.driver;
	
	//显示等待时间
	private final int TIMEOUT = AppDriver.driverWait;
	
	//可参考实际情况,考虑是否使用MobileElement抽象类强转的方式实例化,MobileElement存在2个子类AndroidElement/IOSElement,WebElement是一个通用的接口,其主要实现由RemoteElement实现的
	//MobileElement fieldTwo = (MobileElement) driver.findElementsByClassName("UIATextField").get(1);
	
	/**
	 * 获取 WebElement对象
	 * @author AiHui
	 * @return WebElement
	 */
	public WebElement getWebElement(){
		if(getType().equals("iosUIA")){
			return AppDriver.getElementByUIA_IOS(getLocator());
		}else if(getType().equals("andUIA")){
			return AppDriver.getElementByUIA_AND(getLocator());
		}else if(getType().equals("accessId")){
			return AppDriver.getElementByAccessibilityId(getLocator());
		}else{
			return driver.findElement(getBy());
		}
	}
	
	/**
	 * 获取当前元素及其同级元素并存入 WebElement List中
	 * @author AiHui
	 * @return WebElement List
	 */
	public List<WebElement> getWebElementList(){
		if(getType().equals("iosUIA")){
			return AppDriver.getElementsByUIA_IOS(getLocator());
		}else if(getType().equals("andUIA")){
			return AppDriver.getElementsByUIA_AND(getLocator());
		}else if(getType().equals("accessId")){
				return AppDriver.getElementsByAccessibilityId(getLocator());
		}else{
			return driver.findElements(getBy());
		}
	}
	
	/**
	 * 实例化Item对象
	 * @author AiHui
	 */
	public void initItem(){
		item = (Item) this;
	}
	
	/**
	 * 获取type
	 * @author AiHui
	 * @return type
	 */
	public String getType(){
		initItem();
		ListenerAdapter.type = item.locatorType;
		return item.locatorType;
	}
	
	/**
	 * 获取locatorValue
	 * @author AiHui
	 * @return locatorValue
	 */
	public String getLocator(){
		initItem();
		ListenerAdapter.locator = item.locatorValue;
		return item.locatorValue;
	}
	
	/**
	 * 通过文件指定的元素定位类型,类型值获取By对象
	 * @author AiHui
	 * @return By
	 */
	public By getBy(){
		return getBy(getType(),getLocator());
	}
	
	/**
	 * 通过文件指定的元素定位类型,类型值获取By对象
	 * @author AiHui
	 * @return By
	 *//*
	public By getBy(){
		item = (Item) this;
		ListenerAdapter.locator = item.locatorValue;
		ListenerAdapter.type = item.type;	
		return getBy(item.locatorValue,item.type);
	}*/
	
	/**
	 * 通过指定的元素定位类型,定位对应类型值,获取定位By
	 * @author AiHui
	 * @param locatorValue
	 * @param locatorType
	 * @return By
	 */
	public By getBy(String locatorType,String locatorValue){
		if(locatorType==null){
			return By.xpath(locatorValue);
		}else{
			locatorType = locatorType.toLowerCase();
			if(locatorType.equals("xpath")){
				return By.xpath(locatorValue);
			}else if(locatorType.equals("id")){
				return By.id(locatorValue);
			}else if(locatorType.equals("link")){
				return By.linkText(locatorValue);
			}else if(locatorType.equals("partial")){
				return By.partialLinkText(locatorValue);
			}else if(locatorType.equals("name")){
				return By.name(locatorValue);
			}else if(locatorType.equals("tag")){
				return By.tagName(locatorValue);
			}else if(locatorType.equals("class")){
				return By.className(locatorValue);
			}else if(locatorType.equals("css")){
				return By.cssSelector(locatorValue);
			}else{
				return By.xpath(locatorValue); 
			}
		}
	}
		
	/**
	 * 单击
	 * @author AiHui
	 */
	public void click(){
		getWebElement().click();
	}
	
	/**
	 * 单击n次
	 * @author ZhaoHaiYang
	 */
	public void click(int n){
		for(int i = 0; i < n; i++)
			getWebElement().click();
	}
	
	/**
	 * 清空内容
	 * @author AiHui
	 */
	public void clear(){
		getWebElement().clear();
	}	
	
	/**
	 * 清空可编辑框,再输入相应内容,且检查输入结果的正确性
	 * @author AiHui
	 * @param text
	 */
	public void input(String text) {
		inputWithoutCheck(text);
		valueToBe(text);
	}
	
	/**
	 * 输入值但不验证输入结果
	 * @author AiHui
	 * @param text
	 */	
	public void inputWithoutCheck(String text) {
		WebElement e = getWebElement();
		e.clear();
		e.sendKeys(text);
	}
	/**
	 * 捏屏幕 (双指往内移动来缩小屏幕,2个手指操作控件,从对角线向中心点滑动)
	 * @author aihui
	 */
	public void pinch(){
		WebElement e = getWebElement();
		driver.pinch(e);
	}
	
	/**
	 * 以(x,y)为基准,计算得出(x,y-100),(x,y+100)两个点,然后2个手指按住这两个点同时滑到(x,y)
	 * @author aihui
	 * @param x
	 * @param y
	 */
	public void pinchByXY(int x, int y){
		driver.pinch(x, y);
	}
	
	/**
	 * 放大屏幕 (双指往外移动来放大屏幕),与pinch(webElement)的动作刚好相反,两个手指由控件的中心点慢慢向控件的左顶点后右底点滑动
	 * @author aihui
	 */
	public void zoom(){
		WebElement e = getWebElement();
		driver.zoom(e);
	}
	
	/**
	 * 放大屏幕 (双指往外移动来放大屏幕),和pinch(x,y)相反,两个手指从(x,y)点开始向(x,y-100)和(x,y+100)滑动
	 * @author aihui
	 */
	public void zoomByXY(int x,int y){
		driver.zoom(x,y);
	}

	/**
	 * 点击(x,y)点按下,duration*5毫秒后松开,如此重复fingers次
	 * @author aihui
	 * @param fingers
	 * @param x
	 * @param y
	 * @param duration
	 */
	public void tap(int fingers, int x, int y, int duration){
		driver.tap(fingers, x, y, duration);
	}
	
	/**
	 * 在element控件中心点按下,duration*5毫秒秒后松开,如此重复fingers次
	 * @author aihui
	 * @param fingers
	 * @param duration
	 */
	public void tap(int fingers, int duration){
		WebElement e = getWebElement();
		driver.tap(fingers, e, duration);
	}
	
	/**
	 * 滑动到某个控件元素
	 * @author aihui
	 */
	public void scrollToByJSExecutor(){
		WebElement e = getWebElement();
		HashMap<String, String> arguments = new HashMap<String, String>();
		arguments.put("element", ((RemoteWebElement) e).getId());
		((JavascriptExecutor)driver).executeScript("mobile: scrollTo", arguments);
		//driver.executeScript("mobile: scrollTo", arguments);
	}
	
	/*First of all sorry for the late reply, As per my knowledge driver.scrollTo(value) only working when android.widget.ListView have single text in it and it fails when multiple text is present in each row of listView. In my case I have single entry/text in each line.
	So to resolve your issue you can use below method which I implemented for me and it is working fine.
	**public WebElement scroll(String keyword){
	      HashMap scrollObject = new HashMap();
		  RemoteWebElement element = (RemoteWebElement) driver.findElementByAndroidUIAutomator("new UiSelector().className(\"android.widget.ListView\")");
		  JavascriptExecutor js = (JavascriptExecutor) driver;
	      String webElementId = ((RemoteWebElement) element).getId();
		  System.out.println(webElementId);
		  System.out.println(element);
		  scrollObject.put("text", keyword);
		  scrollObject.put("element", webElementId);
		  js.executeScript("mobile: scrollTo", scrollObject);
		  return element;
	  }***/

	/**
	 * 拖动应用图至期望图标位置
	 * @author aihui
	 * @param WebElement e1
	 * @param WebElement e2
	 * 拖动相机图标至日历图标位置,
	 * new TouchAction(driver).longPress(driver.findElementByName("相机"))
	 * .moveTo(driver.findElementByName("日历")).release().perform();
	 */
	public void fromAMoveToB(WebElement e1,WebElement e2){
		getAction().longPress(e1).moveTo(e2).release().perform();
	}
	
	/**
	 * 以(x,y)点为目标,从另一个点移动到该目标上
	 * @author aihui
	 */
	public void moveToXY(int x,int y){
		getAction().moveTo(x, y);
	}
	
	/**
	 * 以控件el的左上角为基准,x轴向右移动x单位,y轴向下移动y单位
	 * @author aihui
	 */
	public void moveTo(int x,int y){
		WebElement e1 = getWebElement();
		getAction().moveTo(e1, x, y);
	}
	
	/**
	 * 以el为目标,从另一个点移动到该目标上
	 * @author aihui
	 */
	public void moveTo(){
		WebElement e1 = getWebElement();
		getAction().moveTo(e1);
	}
	
	/**
	 * 在控件e的左上角的x坐标偏移x单位，y左边偏移y单位的坐标上执行press,release
	 * 触摸动作(TOUCHACTION) / 多点触摸动作(MULTITOUCHACTION)
	 * Demo: WebElement mapview = null;
		     TouchAction action = new TouchAction(driver).press(mapview, 10, 10).release().perform();
	 * @author aihui
	 * @param  int x, int y
	 */
	public void touch(int x, int y){
		WebElement e = getWebElement();
		getAction().press(e, x, y).release().perform();
	}
	
	/**
	 * 在坐标为（x,y）的点执行press,release操作
	 * @author aihui
	 * @param  int x, int y
	 */
	public void touchXY(int x, int y){
		getAction().press(x, y).release().perform();
	}
	
	/**
	 * 在控件上执行press,release操作
	 * @author aihui
	 * @param  int x, int y
	 */
	public void touch(){
		WebElement e = getWebElement();
		getAction().press(e).release().perform();
	}
	
	/**
	 * 以控件e为目标长按
	 * @author aihui
	 */
	public void longTouch(){
		WebElement e = getWebElement();
		getAction().longPress(e).release().perform();
	}
	
	/**
	 * 以(x,y)点为目标,长按
	 * @author aihui
	 */
	public void longTouchXY(int x,int y){
		getAction().longPress(x,y).release().perform();
	}
	 
	/**
	 * 在控件e的左上角的x坐标偏移x单位，y左边偏移y单位的坐标上执行长按
	 * @author aihui
	 */
	public void longTouch(int x,int y){
		WebElement e = getWebElement();
		getAction().longPress(e,x,y);
	}
	
	/**
	 * 获取TouAction对象
	 * @author AiHui
	 */
	public TouchAction getAction(){
		return 	new TouchAction(driver);
	}
	
	/**
	 * 代表一个空操作，等待一段时间
	 * @author AiHui
	 */
	public void waitAction(){
		getAction().waitAction();
	}
	
	/**
	 * 等待指定的时间ms前通过,继续下一个触摸动作
	 * @author AiHui
	 */
	public void waitAction(int ms){
		getAction().waitAction(ms);
	}
	
	/**
	 * 获取元素text
	 * @author AiHui 
	 * @return element text
	 */
	public String getText(){
		return getWebElement().getText();
	}
	
	/**
	 * 图案解锁Demo,需将坐标参数化优化,参考实际产品业务
	 * @author aihui
	 */
	public void unlockPattern(){
		getAction().press(215, 870).moveTo(540, 870).moveTo(862, 870)
		.moveTo(540, 1195).moveTo(215, 1195).moveTo(215, 1518)
        .moveTo(540, 1518).moveTo(862, 1518).moveTo(862, 1195)
        .release().perform();
	}
	
	
	
	
	
	//(以下方法,主要适用于webview 元素操作,对于原生APP控件操作不适应)
	/**
	 * Get the tag name of this element. <b>Not</b> the value of the name attribute: will return
	 * <code>"input"</code> for the element <code>&lt;input name="foo" /&gt;</code>.
	 * @author AiHui 
	 * @return The tag name of this element.
	 */
	public String getTagName(){
		return getWebElement().getTagName();
	}
	
	/**
	 * Where on the page is the top left-hand corner of the rendered element?
	 * @author AiHui 
	 * @return A point, containing the location of the top left-hand corner of the element
	 */	
	public Point getLocation(){
		return getWebElement().getLocation();
	}
	
	/**
	 * What is the width and height of the rendered element?
	 * @author AiHui 
	 * @return The size of the element on the page.
	 */
	public Dimension getSize(){
		return getWebElement().getSize();
	}

	/**
	 * Get attribute value. 
	 * @author AiHui
	 * @param name The name of the attribute.
	 * @return The attribute/property's current value or null if the value is not set.
	 */
	public String getAttribute(String attributeName){
		return getWebElement().getAttribute(attributeName);
	}
	
	/**
	 * Determine whether or not this element is selected or not. This operation only applies to input
	 * elements such as checkboxes, options in a select and radio buttons.
	 * @author AiHui
	 * @return True if the element is currently selected or checked, false otherwise.
	 */
	public boolean isSelected(){
		return getWebElement().isSelected();
	}
	
	/**
	 * Is this element displayed or not? This method avoids the problem of having to parse an
	 * element's "style" attribute.
	 * @author AiHui
	 * @return True if the element is displayed, false otherwise.
	 */
	public boolean isDisplayed(){
		return getWebElement().isDisplayed();
	}
	
	/**
	 * Is the element currently enabled or not? This will generally return true for everything but
	 * disabled input elements.
	 * @author AiHui
	 * @return True if the element is enabled, false otherwise.
	 */
	public boolean isEnableed(){
		return getWebElement().isEnabled();
	}
	
	/**
	 * If this current element is a form, or an element within a form, then this will be submitted to
	 * the remote server. If this causes the current page to change, then this method will block until
	 * the new page is loaded.
	 * 
	 * @throws NoSuchElementException If the given element is not within a form
	 */
	public void submit(){
		WebElement e = getWebElement();
		e.submit();
	}
	
	/**
	 * 验证元素存在,直到超时
	 * @author aihui
	 */
	public boolean isElementPresent(int timeout){
	    try {
		    driver.manage().timeouts().implicitlyWait(timeout,TimeUnit.SECONDS);
			getWebElement();
			driver.manage().timeouts().implicitlyWait(30,TimeUnit.SECONDS);
			return true;
		} catch (NoSuchElementException e) {
			driver.manage().timeouts().implicitlyWait(30,TimeUnit.SECONDS);
			return false;
		}
	}

	/**
	 * 验证元素加载完成,直到超时
	 * @author AiHui
	 */
	public void toBePresent() {
		//new WebDriverWait(driver, TIMEOUT).until(ExpectedConditions.presenceOfElementLocated(getBy()));
	
		new WebDriverWait(driver, TIMEOUT).until(ExpectedConditions.elementToBeSelected(getBy()));
		
	}
	
	/**
	 * 验证元素不存在直到超时
	 * @author AiHui
	 */
	public void toBeNotPresent() {
		new WebDriverWait(driver, TIMEOUT).until(new Function<WebDriver, Boolean>() {
			@Override
			public Boolean apply(WebDriver driver) {
				return !isElementPresent(2);
			  }
			});
	}
	
	/**
	 * 验证元素(下拉列表项,单选框,复选框)被选中,直到超时
	 * @author AiHui
	 */
	public void toBeSelected() {
		new WebDriverWait(driver, TIMEOUT).until(ExpectedConditions.elementToBeSelected(getBy()));
	}
	
	/**
	 * 验证元素(下拉列表项,单选框,复选框)不被选中,直到超时
	 * @author AiHui
	 */
	public void toBeNotSelected() {
		new WebDriverWait(driver, TIMEOUT).until(ExpectedConditions.elementSelectionStateToBe(getBy(),false));
	}
	
	/**
	 * 验证焦点移至当前元素,直到超时
	 * 等待元素区域活跃,切换至当前文档中的元素或body元素,此调用匹配javascript avtiveElement
	 * @author AiHui
	 */
	public void toBeActive() {
		new WebDriverWait(driver, TIMEOUT).until(new Function<WebDriver, Boolean>() {
			public Boolean apply(WebDriver driver) {
				return driver.switchTo().activeElement().getLocation().equals(getWebElement().getLocation());
			  }
			});
	}
	
	/**
	 * 验证元素可用,直到超时
	 * @author AiHui
	 */
	public void toBeEnable() {
		new WebDriverWait(driver, TIMEOUT).until(ExpectedConditions.elementToBeClickable(getBy()));
	}
	
	/**
	 * 验证元素不可用,直到超时
	 * @author AiHui
	 */
	public void toBeDisable() {
		new WebDriverWait(driver, TIMEOUT).until(new Function<WebDriver, Boolean>() {
			public Boolean apply(WebDriver driver) {
				return !getWebElement().isEnabled();
			  }
		});
	}
	
	/**
	 * 验证元素可见,直到超时
	 * @author AiHui
	 */
	public void toBeVisible() {
		new WebDriverWait(driver, TIMEOUT).until(ExpectedConditions.visibilityOfElementLocated(getBy()));
	}
	
	/**
	 * 验证元素不可见,直到超时
	 * @author AiHui
	 */
	public void toBeInvisible() {
		new WebDriverWait(driver, TIMEOUT).until(ExpectedConditions.invisibilityOfElementLocated(getBy()));
	}

	/**
	  * 先获取元素对象,再调用javascript
	  * @author AiHui
	  * @param script to execute javascript
	  */
	public void jsExecutor(String script) {
		try {
			WebElement e = getWebElement();
			((JavascriptExecutor) driver).executeScript(script, e);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 闪烁元素
	 * @author AiHui
	 * @param locator
	 * @param type
	 */
	public void twinkleElement(String locator,String type) {
		try {
			WebElement e = driver.findElement(getBy(locator,type));
			((JavascriptExecutor) driver).executeScript(
					"arguments[0].style.border = '4px solid red'", e);
			Thread.sleep(100);
			((JavascriptExecutor) driver).executeScript(
					"arguments[0].style.border = 'none'", e);
		} catch (NoSuchElementException e) {
			ListenerAdapter.locator = null;
			return;
		} catch (Exception e) {
		}
	}
	
	/**
	 * 使当前元素高亮
	 * @author AiHui
	 * @param locator
	 * @param type
	 */
	public void lightElement(String locator,String type) {
		if (locator == null) {
			return;
		}
		try {
			WebElement e = driver.findElement(getBy(locator,type));
			((JavascriptExecutor) driver).executeScript(
					"arguments[0].style.border = '4px solid red'", e);
		} catch (Exception e) {
		}
	}
	
	/**
	 * 验证元素text与期望text相同,直到超时
	 * @author AiHui
	 * @param text
	 * expected text
	 */
	public void textToBe(String text) {
		new WebDriverWait(driver, TIMEOUT).until(ExpectedConditions.textToBePresentInElement(getBy(),text));
	}
	
	/**
     * 验证元素text不为空,直到超时
     * @author AiHui
     */
    public void textToBeNotEmpty() {
        new WebDriverWait(driver, TIMEOUT).until(new Function<WebDriver, Boolean>() {
            public Boolean apply(WebDriver driver) {
                return getText().length()!=0;
            }
        });
    }
	
	/**
	 * 验证元素value包含期望value,直到超时
	 * @author AiHui
	 * @param value
	 *            expected contained value
	 */
	public void valueContains(final String value) {
		new WebDriverWait(driver, TIMEOUT).until(new Function<WebDriver, Boolean>() {
			public Boolean apply(WebDriver driver) {
				return getAttribute("value").contains(value);
			  }
			});
	}
	
	/**
	 * 验证元素value与期望value相同,直到超时
	 * @author AiHui
	 * @param value
	 *            expected value
	 */
	public void valueToBe(final String value) {
		new WebDriverWait(driver, TIMEOUT).until(new Function<WebDriver, Boolean>() {
			public Boolean apply(WebDriver driver) {
				return getAttribute("value").equals(value);
			  }
			});
	}
	
	/**
     * 验证元素value不为空,直到超时
     * @author AiHui
     */
    public void valueToBeNotEmpty() {
        new WebDriverWait(driver, TIMEOUT).until(new Function<WebDriver, Boolean>() {
            public Boolean apply(WebDriver driver) {
                return getAttribute("value").length()!=0;
            }
        });
    }
    
    /**
	 * 拖动元素至视图可见
	 * @author AiHui
	 */
	public void scrollIntoView() {
		jsExecutor("arguments[0].scrollIntoView();");
	}
	
	/**
	 * 获取 WebElement对象
	 * @author AiHui
	 * @return WebElement
	 *//*
	public WebElement getWebElement(){
		item = (Item)this;
		ListenerAdapter.locator = item.locatorValue;
		ListenerAdapter.type = item.type;
		return getWebElement(item.locatorValue,item.type);	
	}
	
	*//**
	 * 获取 WebElement对象
	 * @author AiHui
	 * @return WebElement List
	 *//*
	public List<WebElement> getWebElementList(){
		item = (Item)this;
		ListenerAdapter.locator = item.locatorValue;
		ListenerAdapter.type = item.type;
		return getWebElementList(item.locatorValue,item.type);	
	}

	*//**
	 * 获取 WebElement对象
	 * @author AiHui
	 * @param locatorValue 
	 * @param locatorType
	 * @return WebElement
	 *//*
	public WebElement getWebElement(String locatorValue,String locatorType){
		if(locatorType==null){
			return driver.findElementByXPath(locatorValue);
		}else{
			locatorType = locatorType.toLowerCase();
			if(locatorType.equals("name")){
				return driver.findElementByName(locatorValue);
			}else if(locatorType.equals("class")){
				return driver.findElementByClassName(locatorValue);
			}else if(locatorType.equals("id")){
				return driver.findElementById(locatorValue);
			}else if(locatorType.equals("access")){
				return driver.findElementByAccessibilityId(locatorValue);
			}else if(locatorType.equals("css")){
				return driver.findElementByCssSelector(locatorValue);
			}else if(locatorType.equals("link")){
				return driver.findElementByLinkText(locatorValue);
			}else if(locatorType.equals("partial")){
				return driver.findElementByPartialLinkText(locatorValue);
			}else if(locatorType.equals("tag")){
				return driver.findElementByTagName(locatorValue);
			}else if(locatorType.equals("xpath")){
				return driver.findElementByXPath(locatorValue);
			}else{
				return driver.findElementByXPath(locatorValue);
			}
		}		
	}
	
	*//**
	 * 获取当前元素及其同级元素并存入 WebElement List中
	 * @author AiHui
	 * @param locatorValue 
	 * @param locatorType
	 * @return WebElement List
	 *//*
	public List<WebElement> getWebElementList(String locatorValue,String locatorType){
		if(locatorType==null){
			return driver.findElementsByXPath(locatorValue);
		}else{
			locatorType = locatorType.toLowerCase();
			if(locatorType.equals("name")){
				return driver.findElementsByName(locatorValue);
			}else if(locatorType.equals("class")){
				return driver.findElementsByClassName(locatorValue);
			}else if(locatorType.equals("id")){
				return driver.findElementsById(locatorValue);
			}else if(locatorType.equals("access")){
				return driver.findElementsByAccessibilityId(locatorValue);
			}else if(locatorType.equals("css")){
				return driver.findElementsByCssSelector(locatorValue);
			}else if(locatorType.equals("link")){
				return driver.findElementsByLinkText(locatorValue);
			}else if(locatorType.equals("partial")){
				return driver.findElementsByPartialLinkText(locatorValue);
			}else if(locatorType.equals("tag")){
				return driver.findElementsByTagName(locatorValue);
			}else if(locatorType.equals("xpath")){
				return driver.findElementsByXPath(locatorValue);
			}else{
				return driver.findElementsByXPath(locatorValue);
			}
		}
	}*/
}
